package com.qf.smartplatform.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.smartplatform.cache.CategoryCache;
import com.qf.smartplatform.cache.DeviceCache;
import com.qf.smartplatform.cache.SceneCache;
import com.qf.smartplatform.constans.ResultCode;
import com.qf.smartplatform.event.DeviceColorEvent;
import com.qf.smartplatform.event.DeviceCommandEvent;
import com.qf.smartplatform.exceptions.AddException;
import com.qf.smartplatform.exceptions.DeleteDataException;
import com.qf.smartplatform.exceptions.FindDataException;
import com.qf.smartplatform.exceptions.UpdateDateException;
import com.qf.smartplatform.mapper.DeviceMapper;
import com.qf.smartplatform.pojo.*;
import com.qf.smartplatform.service.DeviceService;
import com.qf.smartplatform.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/*** @author Tgb
 * @version V1.0
 * @Project smartplatform
 * @Package com.qf.smartplatform.service.impl
 * @Description:
 * @Date 2022/9/9 19:42
 */
@Service
@Transactional
public class DeviceServiceImpl implements DeviceService {
    private ObjectMapper objectMapper;
    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    private CategoryCache categoryCache;
    @Autowired
    public void setCategoryCache(CategoryCache categoryCache) {
        this.categoryCache = categoryCache;
    }

    private ApplicationContext applicationContext;
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    private SceneCache sceneCache;
    @Autowired
    public void setSceneCache(SceneCache sceneCache) {
        this.sceneCache = sceneCache;
    }
    private DeviceMapper deviceMapper;
    @Autowired
    public void setDeviceMapper(DeviceMapper deviceMapper) {
        this.deviceMapper = deviceMapper;
    }
    private DeviceCache deviceCache;
    @Autowired
    public void setDeviceCache(DeviceCache deviceCache) {
        this.deviceCache = deviceCache;
    }

    @Override
    public void addDevice(SysDevice sysDevice) {
        BaseUser loginUser = SecurityUtils.getUser();
        sysDevice.setBindUserId(loginUser.getuId());

        Long sceneId = sysDevice.getSceneId();

        if (sceneId!=null&&(-1>sceneId ||0==sceneId)) {
            //传递了id不符合要求
            throw new AddException("场景不存在", ResultCode.DATA_NOT_EXIST);
        } else if (sceneId!=null&&sceneId > 0) {
            //传递了一个场景id,但是这个场景不一定属于谁
            //判断是否属于当前用户
//            sceneService.getSceneById(sceneId);
            //如果传递了场景id,我们还得判断当前的场景id属于这个用户
            List<SysScene> sysScenes = sceneCache.get(loginUser.getuId());
            List<SysScene> scenes = sysScenes.stream().filter(sysScene -> sysScene.getSceneId() == sceneId).collect(Collectors.toList());
            SysScene scene = scenes.get(0);
            Assert.notNull(scene,()->{
                throw new FindDataException("该用户下没有查询到对应的场景", ResultCode.DATA_NOT_EXIST);
            });

        }
        //这个设备可能已经被绑定到了其它人身上
        SysDevice device = deviceMapper.findByDeviceId(sysDevice.getDeviceId());
        Assert.isTrue(device==null||device.getBindUserId().equals(sysDevice.getBindUserId()),()->{
                throw new AddException("设备已被绑定",ResultCode.DATA_NOT_EXIST);
        });
        if (device==null){
            deviceMapper.addDevice(sysDevice);
        }
    }

    @Override
    public SysDevice findById(String deviceId) {
        BaseUser loginUser = SecurityUtils.getUser();

        List<SysDevice> sysDevices = deviceCache.get(loginUser.getuId());
        Assert.notNull(sysDevices,()->{
            throw new FindDataException("该用户没有绑定设备",ResultCode.DATA_NOT_EXIST);
        });
        List<SysDevice> collect = sysDevices.stream().filter(sysDevice -> sysDevice.getDeviceId().equals(deviceId)).collect(Collectors.toList());
        Assert.isTrue(collect.size()==1,()->{
            throw new FindDataException("该设备不存在",ResultCode.DATA_NOT_EXIST);
        });
        SysDevice sysDevice = collect.get(0);

        return sysDevice;
    }

    @Override
    public List<SysDevice> findAll() {
        BaseUser loginUser = SecurityUtils.getUser();
        return deviceCache.get(loginUser.getuId());
    }

    @Override
    public void deleteById(String deviceId) {
        int result = deviceMapper.deleteById(deviceId);
        if (result==0){
            throw new DeleteDataException("未找到符合条件得设备，删除失败",ResultCode.DELETE_FAIL);
        }

    }

    @Override
    public int update(SysDevice sysDevice) {
        SysDevice byDeviceId = deviceMapper.findByDeviceId(sysDevice.getDeviceId());
        Assert.notNull(byDeviceId,()->{
            throw new FindDataException("未查询到符合更新条件得数据",ResultCode.DATA_NOT_EXIST);
        });
        int result = deviceMapper.updateDevice(sysDevice);

        if (result==0){
            throw new UpdateDateException("更新失败",ResultCode.UPDATE_FAIL);
        }
        BaseUser loginUser = SecurityUtils.getUser();
        sceneCache.refresh(loginUser.getuId());
        return result;
    }

    @Override
    public void updateDeviceStatus(String deviceId, int status) {
        deviceMapper.updateDeviceStatus(deviceId,status);
    }

    @Override
    public void sendCommand(String deviceId, String command) {

        //commnad实际上就是open,close等
        //根据设备的id找到设备的会话,然后把我们的command当做内容发过去
        //我们可以发送一个事件过去,让websocket自己处理
        //我们需要将命令转成实际的数据,到底怎么转换,取决于我们实际定义的情况
        //TODO 根据设备的id查询到设备的分类id,然后根据分类id查询到命令,然后根据传递过来的命令的名字来获取到真正的命令,然后给设备发送过

        SysDevice device = deviceMapper.findByDeviceId(deviceId);
        SysCategory category = categoryCache.getById(device.getCategyId());
        String commandName = category.getCommandName();
        try {
            Map<String,String> commandNameMap = objectMapper.readValue(commandName, Map.class);
            String finalCommand = command;
            String commandNameKey = commandNameMap.entrySet()
                    .stream()
                    .filter(key -> finalCommand.equals(key.getValue()))
                    .map(Map.Entry::getKey)
                    .findFirst().get();

            String txCommand = category.getTxCommand();
            Map<String,Map> txCommandMap = objectMapper.readValue(txCommand, Map.class);
            command = (String) txCommandMap.get(commandNameKey).get("command");
            Integer type = (Integer) txCommandMap.get(commandNameKey).get("type");
            if (type!=null){
                if (type==1){
                    applicationContext.publishEvent(new DeviceCommandEvent(deviceId,command));
                }
                if (type==2){
                    applicationContext.publishEvent(new DeviceColorEvent(deviceId,command));
                }
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}
